using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;
using depthcamsensor = Microsoft.Robotics.Services.DepthCamSensor;
using depthcamsensorproxy = Microsoft.Robotics.Services.DepthCamSensor.Proxy;
using webcamsensor = Microsoft.Robotics.Services.WebCamSensor;
using webcamsensorproxy = Microsoft.Robotics.Services.WebCamSensor.Proxy;
using Microsoft.Ccr.Adapters.Wpf;
using System.Windows;

namespace KinectCamRecorder
{
    [Contract(Contract.Identifier)]
    [DisplayName("KinectCamRecorder")]
    [Description("KinectCamRecorder service (no description provided)")]
    class KinectCamRecorderService : DsspServiceBase
    {
        [ServiceState]
        KinectCamRecorderState _state = new KinectCamRecorderState();

        [ServicePort("/KinectCamRecorder", AllowMultipleInstances = true)]
        KinectCamRecorderOperations _mainPort = new KinectCamRecorderOperations();

        WpfServicePort _wpfServicePort;
        KinectCamRecorderUI _wpfUI;
        /// <summary>
        /// DepthCamSensor partner
        /// </summary>
        [Partner("DepthCam", Contract = depthcamsensor.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExisting, Optional = true)]
        depthcamsensorproxy.DepthCamSensorOperationsPort depthCamSensorPort = new depthcamsensorproxy.DepthCamSensorOperationsPort();

        /// <summary>
        /// WebCamSensor partner
        /// </summary>
        [Partner("WebCam", Contract = webcamsensor.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExisting, Optional = true)]
        webcamsensorproxy.WebCamSensorOperations webCamSensorPort = new webcamsensorproxy.WebCamSensorOperations();

        public KinectCamRecorderService(DsspServiceCreationPort creationPort)
        : base(creationPort)
        {
        }

        protected override void Start()
        {
            SpawnIterator(Initialize);
        }

        IEnumerator<ITask> Initialize()
        {
            #region Create UI
            // create WPF adapter
            _wpfServicePort = WpfAdapter.Create(TaskQueue);

            var runWindow = _wpfServicePort.RunWindow(() => new KinectCamRecorderUI(_mainPort));
            yield return (Choice)runWindow;

            var exception = (Exception)runWindow;
            if (exception != null)
            {
                LogError(exception);
                StartFailed();
                yield break;
            }

            // need double cast because WPF adapter doesn't know about derived window types
            _wpfUI = (Window)runWindow as KinectCamRecorderUI;
            #endregion

            StartComplete();
        }

        void StartComplete()
        {
            base.Start();

            Activate(Arbiter.Interleave(
                new TeardownReceiverGroup(
                    Arbiter.Receive<DsspDefaultDrop>(false, _mainPort, DropHandler)),
                new ExclusiveReceiverGroup(
                    Arbiter.ReceiveWithIterator<AddFrame>(true, _mainPort, AddFrameHandler)),
                new ConcurrentReceiverGroup()));

            DirectoryInsert();

            if (webCamSensorPort != null || depthCamSensorPort != null)
                Activate(Arbiter.ReceiveWithIterator(false, TimeoutPort(100), GetKinectFrames));
        }

        public IEnumerator<ITask> GetKinectFrames(DateTime timeout)
        {
            depthcamsensor.DepthCamSensorState depthCamFrame = null;

            if (depthCamSensorPort != null)
                yield return Arbiter.Choice(
                    depthCamSensorPort.Get(),
                    delegate(depthcamsensor.DepthCamSensorState success)
                    {
                        depthCamFrame = success;
                    },
                    delegate(Fault f)
                    {
                    });

            webcamsensor.WebCamSensorState webCamFrame = null;

            if (webCamSensorPort != null)
                yield return Arbiter.Choice(
                    webCamSensorPort.Get(),
                    delegate(webcamsensor.WebCamSensorState success)
                    {
                        webCamFrame = success;
                    },
                    delegate(Fault f)
                    {
                    });

            _wpfServicePort.Invoke(
                delegate()
                {
                    _wpfUI.AddWebCamFrame(webCamFrame);
                    _wpfUI.AddDepthCamFrame(depthCamFrame);
                });

            Activate(Arbiter.ReceiveWithIterator(false, TimeoutPort(100), GetKinectFrames));
        }
      
        void DropHandler(DsspDefaultDrop drop)
        {
            if (_wpfUI != null)
            {
                _wpfServicePort.Invoke(
                    delegate()
                    {
                        _wpfUI.Close();
                    });

                base.DefaultDropHandler(drop);
            }
        }

        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ITask> AddFrameHandler(AddFrame update)
        {
            _wpfServicePort.Invoke(
                delegate()
                {
                    _wpfUI.AddDepthCamFrame(update.Body.DepthCamFrame);
                    _wpfUI.AddWebCamFrame(update.Body.WebCamFrame);
                });

            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            yield break;
        }
    }
}